/*************************************************************
Purpose:
		Make baseline summary table
Model:
		TEST
Date:
		20230430 First version;
		20230504 fix some bugs[format error,name error];
		20231102 add arguments debug; fix group label display;
**************************************************************/
%macro makeBaseline(
					indata= 			 /*The dataset you are interested in*/
					,catVars= 			 /*Some categorical variables separated by | */
					,qanVars=  			 /*Some quantitative variables separated by |*/
					,grpVar= 			 /*A group variable*/
					,perType=col   		 /*The type of frequencies: col/row. Default:col*/
					,normal=S   		 /*Normal test method:S=Shapiro-Wilk/K=Kolmogorov-Smirnov*/
					,grplabels= 		 /*The labels for Group variables*/
					,file=               /*The output file path (not contains file type)*/
					,title=     		 /*The title of table*/
					,page=portrait       /*The orieation of page. default:portrait, landscape be allowed*/
				    ,width=99            /*The width of table covering the paper*/
					,debug=0 			 /*debug=0:Delete the dataset during runtime*/
					)/des="Make baseline summary table"; 
	ods escapechar="^";
	options  nomexecnote validvarname=any;
	%local i numOFCatVARS numOFQanVARS tempfmt;
	%local chisqVars correctchisqVars;
	%if %length(&indata)=0 %then %do;
		%put ERROR:INDATA MUST SPECIFY!;
		%abort cancel;
	%end;
	%if %length(&catVars)=0 & %length(&qanVars)=0 %then %do;
		%put ERROR:AT LEAST ONE OF CATVARS OR QANVARS SPECIFY!;
		%abort cancel;
	%end;
	%let dsid=%sysfunc(open(&indata,i));
	%if &dsid=0 %then %do;
		%put ERROR:PLEASE CHECK WHETHER THE SPELLING OF &indata IS RIGHT;
		%abort cancel; 
	%end;%else 
	%do;
		%let numOfCatVARS=%sysfunc(countw(&catVars,|));
		%if %length(&catVars)>0 %then %do;
			%do i=1 %to &numOFCatVARS;
				%let rc=%sysfunc(varnum(&dsid,%scan(&catVars,&i,|)));
				%if &rc=0 %then %do;
					%put ERROR:%scan(&catVars,&i,|) NOT IN &indata;
					%abort cancel;
				%end;
			%end;
		%end;
		%let numOfQanVARS=%sysfunc(countw(&qanVars,|));
		%if %length(&qanVars)>0 %then %do;
			%do i=1 %to &numOfQanVARS;
				%let rc=%sysfunc(varnum(&dsid,%scan(&qanVars,&i,|)));
				%if &rc=0 %then %do;
					%put ERROR:%scan(&qanVars,&i,|) NOT IN &indata;
					%abort cancel;
				%end;
			%end;
		%end;
		%let rc=%sysfunc(close(&dsid));
	%end;
	%if %length(&catVars)>0 %then %do;
		%do i=1 %to &numOfCatVARS;
			%let temp=%scan(&catVars,&i,|);
			proc freq data=&indata;
				table &temp.*&grpVar/out=__freq_&temp outexpect outpct;
			run;
			proc sql noprint;
				select sum(count),
					   count(distinct &temp),
					   min(expected),
					   sum(expected<5),
					   count(*) into :totalN,:nLevels,:minExpected,:minExpectedCells,:Ncells
				from __freq_&temp;
			quit;
			proc sql noprint;
				select cats(fmtname,".") into :tempfmt 
					from sashelp.vcformat 
						where lowcase(fmtname) contains "%lowcase(&temp)fmt";
			quit;
			%if &nLevels=2 %then %do;
				%if &minExpected>=5 and &totalN>40 %then %do;
					%put &temp.--Chisq square Test;
					proc freq data=&indata;
						%if %length(&tempfmt)>0 %then format &temp &tempfmt;;
						table &temp.*&grpVar/chisq;
						output out=__stat_&temp chisq;
					run;
				%end;%else %if &minExpected<5 and &minExpected>=1 and &totalN>40 %then %do;
					%if %length(&tempfmt)>0 %then format &temp &tempfmt;;
					%put &temp.--Adjusted Chisq square Test;
					proc freq data=&indata;
						table &temp.*&grpVar/chisq;
						output out=__stat_&temp(keep= _AJCHI_ P_AJCHI
												rename=(_AJCHI_=stat P_AJCHI=pvalue)) ajchi;
					run;
				%end;%else %do;
					%put &temp.--Fisher Test;
					proc freq data=&indata;
						%if %length(&tempfmt)>0 %then format &temp &tempfmt;;	
						table &temp.*&grpVar/fisher;
						output out=__stat_&temp(rename=(XP2_FISH=pvalue)) fisher;
					run;					
				%end;	
			%end;
			%else %do;
				%if %sysevalf(&minExpectedCells/&Ncells)>0.2 or &minExpected<1 %then %do;
					%put &temp.--Fisher Test;
					proc freq data=&indata;
						%if %length(&tempfmt)>0 %then format &temp &tempfmt;;
						table &temp.*&grpVar/fisher;
						output out=__stat_&temp(rename=(XP2_FISH=pvalue)) fisher;
					run;					
				%end;%else %do;
					%put &temp.--Chisq square Test;
					proc freq data=&indata;
						%if %length(&tempfmt)>0 %then format &temp &tempfmt;;
						table &temp.*&grpVar/chisq;
						output out=__stat_&temp(keep=_pchi_ p_pchi
												rename=(_pchi_=stat p_pchi=pvalue)) 
												chisq;
					run;					
				%end;
			%end;
			data __freq_&temp;
				set __freq_&temp;
				nper=cats(count,"(",put(pct_&perType,8.2),")");
			run;
			proc transpose data=__freq_&temp. out=__freq_&temp. prefix=&grpVar._;
				by &temp;
				var nper;
				id &grpVar;
			run;
			data __cats_&temp;
				length variables $100;
				if _n_=1 then do;
					set __STAT_&temp;
					variables="&temp";
				end;else do;
					set __freq_&temp;
					variables="^{nbspace 4}"||cats(&temp);
					call missing(stat,pvalue);
				end;				
				drop &temp _name_ ;
			run;
		%end;
	%end;
	%if %length(&qanVars)>0 %then %do;
		%do i=1 %to &numOfQanVARS;
			%let temp=%scan(&qanVars,&i,|);
			ods output TestsForNormality=normal;
			proc univariate data=&indata. normal;
				ods select TestsForNormality;
				class &grpVar;
				var &temp;
			run;
			proc sql noprint;
				select min(pValue) into :np from normal where lowcase(test) eqt "%lowcase(&normal)";
				select count(distinct &grpVar) into :levelsOfgrp from &indata;
			quit;
			%if &np.>=0.05 %then %do;
				%if &levelsOfgrp.=2 %then %do;
					ods output ttests=__stat_&temp equality=__varEq_&temp;
					proc ttest data=&indata;
						ods select ttests equality;
						class &grpVar;
						var &temp;
					run;
					ods output close;
					data _null_;
						set __varEq_&temp;
						call symputx("varEq",pvalue);
					run;
					%if &varEq<0.05 %then %do;
						%put &temp.---T-TEST;
						data __stat_&temp;
							length variables $100;
							set __stat_&temp;
							variables=cats(variable);
							if _n_=2;
							keep tvalue probt variables;
							rename tvalue=stat probt=pvalue;
						run;
					%end;%else %do;
						%put &temp.---corrected T-TEST;
						data __stat_&temp;
							length variables $100;
							set __stat_&temp;
							variables=cats(variable);
							if _n_=1;
							keep tvalue probt variables;
							rename tvalue=stat probt=pvalue;
						run;
					%end;
				%end;
				%else %do;
					ods output ModelANOVA=__stat_&temp bartlett=__VAREQ_&temp.;
					proc glm data=&indata ;
						ods select Bartlett ModelANOVA; 
						class &grpvar;
						model &temp.=&grpvar.;
						means &grpvar. / hovtest=bartlett;
					run;
					ods output close;
					data _null_;
						set __varEq_&temp;
						call symputx("varEq",ProbChiSq);
					run;
					%if &varEq>=0.05 %then %do;
						%put &temp.---ANOVA;
						data __stat_&temp;
							set __stat_&temp;
							if _n_=1;
							keep probf Dependent;
							rename probf=pvalue Dependent=variables;
						run;
					%end;%else %do;
						%put &temp.---Kruskal Wallis Test;
						ods output KruskalWallisTest=__stat_&temp.(rename=(variable=variables prob=pvalue ChiSquare=stat) drop=df);
						proc npar1way data=&indata wilcoxon;
							ods select KruskalWallisTest;
							class &grpVar;
							var &temp;
						run;
						ods output close;
					%end; 
				%end;
			%end;
			%else %do;
			%if &levelsOfgrp=2 %then %do;
				%put &temp.---Wilcoxon Test;
				ods output WilcoxonTest=__stat_&temp(keep=variable Z prob2
												  rename=(variable=variables Z=stat prob2=pvalue));
				proc npar1way data=&indata wilcoxon;
					ods select WilcoxonTest;
					class &grpVar;
					var &temp;
				run;
				ods output close;
			%end;%else %do;
				%put &temp.---Kruskal Wallis Test;
				ods output KruskalWallisTest=__stat_&temp(rename=(variable=variables 
												prob=pvalue ChiSquare=stat) drop=df);
				proc npar1way data=&indata wilcoxon;
					ods select KruskalWallisTest;
					class &grpVar;
					var &temp;
				run;
				ods output close;				
			%end;	
			%end;
			proc means data=&indata n mean median std p25 p75 min max;
					class &grpVar;
					var &temp;
					output out=__freq_&temp n=n mean=mean median=median std=std p25=p25 p75=p75 min=min max=max;
			run;
			data __freq_&temp;
				set __freq_&temp;
				count=cats(n);
				mstd=cats(put(mean,8.2),unicode("&#177;",'ncr'),put(std,8.2));
				miqr=cats(put(median,8.2),"(",put(p25,8.2),",",put(p75,8.2),")");
				minmax=cats(put(min,8.2),",",put(max,8.2));
			run;
			proc transpose data=__freq_&temp out=__freq_&temp prefix=&grpvar._;
				where _type_=1;
				var count--minmax;
				id &grpvar;
			run;
			data __qan_&temp;
				length variables $100;
				set  __stat_&temp __freq_&temp;
				if _name_="count" then variables="^{nbspace 4}N";
				else if _name_="mstd" then variables="^{nbspace 4}Mean"||unicode("&#177;",'ncr')||"Std";
				else if _name_="miqr" then variables="^{nbspace 4}Median(P25,P75)";
				else if _name_="minmax" then variables="^{nbspace 4}Min,Max";
				drop _name_;
			run;
		%end;
	%end;
	data __total;
	set %if %length(&catVars)>0 %then __cats_:; %if %length(&qanVars)>0 %then __qan_:;;
	run;
	proc sql noprint;
		select count(*) into :nofgrp separated by "|" from &indata where &grpVar is not null group by &grpVar ;
		select distinct &grpVar into :grplabelsraw separated by "|" from &indata where &grpVar is not null 
			order by &grpVar ;
	quit;
	options nodate nonumber  orientation=&page missing = '';
	ods tagsets.rtf style=journal3a file="&file..rtf";
	title j=center height=12pt "&title";
	proc report data=__total spacing=1 headline headskip split = "|" threads style(report)={width=&width%};
		col variables &grpVar._: stat pvalue;
		define variables /"Variables" left display ;
		%do i=1 %to &levelsOfgrp;
			define &grpVar._%scan(&grplabelsraw,&i,|) /right display %if %length(&grplabels)=0 %then %do; "%scan(&grplabelsraw,&i,|)|(N=%scan(&nofgrp,&i,|))" %end;
																	%else %do; 
																		"%scan(&grplabels,&i,|)|(N=%scan(&nofgrp,&i,|))"
																	%end;;
		%end;
		define stat / display right "Statistics";
		define pvalue /display f=pvalue6.3 "P";
	run;
	title;
	proc datasets lib=work mt=data nowarn nolist nodetails;
		delete __freq_: __stat_: __cats_: __qan_: normal ;
	quit;
	options mexecnote;
%mend;
/*********************************************************
data cars;
	set sashelp.cars;
	if Length<200 then lengthl=1;
	else lengthl=2;
run;
%makeBaseline(
		indata=cars 	
		,catVars=Origin|DriveTrain 	
		,qanVars=Invoice|EngineSize  
		,grpVar=lengthl 
		,perType=col   
		,normal=S   
		,grplabels=A|B 
		,file=D:\baseline 
		,title=Baseline
		,page=portrait
		);
**********************************************************/